package com.xhui.vimarket.zkplus.utils;

import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpSession;

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Desktop;
import org.zkoss.zk.ui.Execution;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.Session;
import org.zkoss.zk.ui.Sessions;
import org.zkoss.zk.ui.WebApp;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.event.ForwardEvent;
import org.zkoss.zk.ui.event.GenericEventListener;
import org.zkoss.zk.ui.sys.ExecutionCtrl;
import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zul.Messagebox;
import org.zkoss.zul.impl.InputElement;

import com.xhui.vimarket.common.PageConstants;

/**
 * ZkUtils集合了zk中常用的一些功能，方便开发中的使用.
 * 
 * @author sunflower
 *         Email:zhangxuehuaemail # gmail 点 com
 *@since zk5.0+
 */
public final class ZkUtils {
    
    /**
     * ZUL_PLUS_PARAMETER_PREFIX.
     * zul通过控件方式实现，将URL传入参数存放到zul参数，前缀
     */
    public static final String ZUL_PLUS_PARAMETER_PREFIX = "ZUL_PLUS_PARAMETER_PREFIX";
    
    /**
     * 私有构造函数.
     */
    private ZkUtils() {
    }
    
    /**
     * 获得当前Execution
     * <p>
     * 客户端请求的执行器（例如ServletRequest），当客户端发送一个请求后，
     * 服务端负责构造这个Execution对象，获取当前执行的信息，然后服务这个请求
     * <p>
     * 一个客户端请求, 例如, HttpServletRequest, 或许关联多个请求 request (
     * {@link org.zkoss.zk.au.AuRequest}). 但是, 这些 ZK 请求来自的页面 ({@link Page}
     * )必须具有相同的desktop
     * <p>
     * 因为一个请求也许来自http或其他协议，Execution通常一个隔离层
     * 
     * @return 当前execution
     * @see Execution
     */
    public static Execution getCurrentExecution() {
        return Executions.getCurrent();
    }
    
    /**
     * 返回当前Exection桌面对象.
     * 
     * @return Desktop
     * @see Execution#getDesktop()
     */
    public static Desktop getCurrentDesktop() {
        return ZkUtils.getCurrentExecution().getDesktop();
    }
    
    /**
     * 获得当前Execution所属的会话.
     * <p>
     * <b>注</b>该Session不同于HttpSession,该session为zk定义的session作用域
     * 
     * @return Session
     * @see Session
     */
    public static Session getCurrentSession() {
        return Sessions.getCurrent();
    }
    
    /**
     * 返回本地session对象，如果不可用返回null,返回的对象依赖客户端类型，如果是就http的
     * ，那么返回javax.servlet.http.HttpSession的一个实例
     * ，如果是portlet，那么返回javax.portlet.PortletSession的实例.
     * 
     * @return Object
     */
    public static Object getNativeSession() {
        if (ZkUtils.getCurrentSession() == null)
            return null;
        
        return ZkUtils.getCurrentSession().getNativeSession();
    }
    
    /**
     * 返回本地session对象，如果不可用返回null,返回的对象依赖客户端类型，如果是就http的
     * ，那么返回javax.servlet.http.HttpSession的一个实例
     * ，如果是portlet，那么返回javax.portlet.PortletSession的实例.
     * 
     * @return Object
     */
    public static HttpSession getHttpSession() {
        if (ZkUtils.getCurrentSession() == null)
            return null;
        return (HttpSession) ZkUtils.getCurrentSession().getNativeSession();
    }
    
    /**
     * 返回本地请求对象，如果不可用返回null.
     * 
     * @return 返回的对象依赖web容器，如果web容器时一个servlet容器，那么返回的对象为ServletRequest
     */
    public static Object getNativeRequest() {
        if (ZkUtils.getCurrentExecution() == null)
            return null;
        return ZkUtils.getCurrentExecution().getNativeRequest();
    }
    
    /**
     * 返回本地响应对象，如果不可用返回null.
     * 
     * @return 返回的对象依赖web容器，如果web容器时一个servlet容器，那么返回的对象为ServletResponse
     */
    public static Object getNativeResponse() {
        if (ZkUtils.getCurrentExecution() == null)
            return null;
        return ZkUtils.getCurrentExecution().getNativeResponse();
    }
    
    /**
     * 获得WebApp对象.
     * 
     * @return WebApp
     * @see Desktop#getWebApp()
     * @see WebApp
     */
    public static WebApp getWebApp() {
        return ZkUtils.getCurrentDesktop().getWebApp();
    }
    
    /**
     * getRealPath.
     * 
     * @param path String
     * @return String
     */
    public static String getRealPath(final String path) {
        return ZkUtils.getWebApp().getRealPath(path);
    }
    
    /**
     * 获得当前请求来自的页面.
     * 
     * @return Page
     */
    public static Page getCurrentPage() {
        return ((ExecutionCtrl) ZkUtils.getCurrentExecution()).getCurrentPage();
    }
    
    /**
     * 返回execution作用域内参数.
     * 
     * @return Map
     * @see Execution#getArg()
     */
    public static Map getExecutionArgs() {
        return ZkUtils.getCurrentExecution().getArg();
    }
    
    /**
     * 获得当前Execution作用域内的属性.
     * 
     * @return Map
     * @see Execution#getAttributes()
     */
    public static Map getExectionAttributes() {
        return ZkUtils.getCurrentExecution().getAttributes();
    }
    
    /**
     * 设置请求属性值.
     * 
     * @param name
     *            请求属性
     * @param value
     *            属性值
     */
    public static void setExecutionAttribute(final String name, final Object value) {
        ZkUtils.getCurrentExecution().setAttribute(name, value);
    }
    
    /**
     * 设置Execution作用域属性值或其父作用域的值.
     * 
     * @param name
     *            请求属性
     * @param value
     *            属性值
     * @param recurse
     *            检查父作用域是否存在该属性，如果存在将替换父作用域的值
     */
    public static void setExecutionAttribute(final String name, final Object value,
        final boolean recurse) {
        ZkUtils.getCurrentExecution().setAttribute(name, value);
    }
    
    /**
     * 获得请求参数.
     * 
     * @return 参数map
     */
    public static Map getExecutionParameterMap() {
        return ZkUtils.getCurrentExecution().getParameterMap();
    }
    
    /**
     * 获得请求参数值.
     * 
     * @param name
     *            请求参数的名字
     * @return 指定名字的参数值
     */
    public static String getExecutionParameter(final String name) {
        return ZkUtils.getExecutionParameter(name, ZkUtils.getCurrentExecution().getArg());
    }
    
    /**
     * 获得请求参数值.
     * 
     * @param name
     *            请求参数的名字
     * @return 指定名字的参数值
     */
    public static String getExecutionParameter(final String name, final Map<?, ?> map) {
        if (map != null && map.containsKey(ZkUtils.ZUL_PLUS_PARAMETER_PREFIX + name)) {
            return map.get(ZkUtils.ZUL_PLUS_PARAMETER_PREFIX + name) + "";
        }
        return ZkUtils.getCurrentExecution().getParameter(name);
    }
    
    /**
     * 获得请求参数值.
     * 
     * @param name
     *            参数的名字
     * @return 字符数组
     */
    public static String[] getExecutionParameterValues(final String name) {
        return ZkUtils.getCurrentExecution().getParameterValues(name);
    }
    
    /**
     * 获得当前请求消息头.
     * 
     * @param name
     *            消息头名字
     * @return 消息头值
     */
    public static String getRequestHeader(final String name) {
        return ZkUtils.getCurrentExecution().getHeader(name);
    }
    
    /**
     * 返回指定请求头名字的所有值.
     * 
     * @param name
     *            请求头的名字
     * @return Iterator
     */
    public static Iterator getRequestHeaders(final String name) {
        return (Iterator) ZkUtils.getCurrentExecution().getHeaders(name);
    }
    
    /**
     *返回所有请求头名字.
     * 
     * @return Iterator
     */
    public static Iterator getRequestHeaderNames() {
        return (Iterator) ZkUtils.getCurrentExecution().getHeaderNames();
    }
    
    /**
     * 添加一个指定名称和值的相应头，允许相应头具有多值.
     * 
     * @param name 名称
     * @param value 值
     */
    public static void addResponseHeader(final String name, final String value) {
        ZkUtils.getCurrentExecution().addResponseHeader(name, value);
    }
    
    /**
     * 返回接收请求的本地ip地址.
     * 
     * @return String
     */
    public static String getLocalAddr() {
        return ZkUtils.getCurrentExecution().getLocalAddr();
    }
    
    /**
     * 获得接收请求的本地host name.
     * 
     * @return String
     */
    public static String getLocalName() {
        return ZkUtils.getCurrentExecution().getLocalName();
    }
    
    /**
     * 获得接收请求的本地端口.
     * 
     * @return int
     */
    public static int getLocalPort() {
        return ZkUtils.getCurrentExecution().getLocalPort();
    }
    
    /**
     * 获得发送请求的客户端ip.
     * 
     * @return String
     */
    public static String getRemoteAddr() {
        return ZkUtils.getCurrentExecution().getRemoteAddr();
    }
    
    /**
     * 获得发送请求的客户端的host name.
     * 
     * @return String
     */
    public static String getRemoteHost() {
        return ZkUtils.getCurrentExecution().getRemoteHost();
    }
    
    /**
     * 设置session 属性.
     * 
     * @param name
     *            属性名
     * @param value
     *            属性值
     */
    public static void setSessionAttribute(final String name, final Object value) {
        ZkUtils.getCurrentSession().setAttribute(name, value);
    }
    
    /**
     * 设置session或父作用域 属性.
     * 
     * @param name
     *            属性名
     * @param value
     *            属性值
     * @param recurse
     *            是否查询父作用域包含name名字的属性，如果包含将替换该值
     */
    public static void setSessionAttribute(final String name, final Object value,
        final boolean recurse) {
        ZkUtils.getCurrentSession().setAttribute(name, value);
    }
    
    /**
     * 返回session作用域对象.
     * 
     * @param name
     *            属性名
     * @param recurse
     *            是否检索父作用域，如果为true， 并且当前作用域没声明这个属性，那么将搜索父作用域
     * @return Object
     */
    public static Object getSessionAttribute(final String name, final boolean recurse) {
        return ZkUtils.getCurrentSession().getAttribute(name);
    }
    
    /**
     * 返回session作用域对象.
     * 
     * @param name
     *            属性名
     * @return Object
     */
    public static Object getSessionAttribute(final String name) {
        return ZkUtils.getCurrentSession().getAttribute(name);
    }
    
    /**
     * 获得所有session作用域对象.
     * 
     * @return map 类型的作用域所有对象
     */
    public static Map getSessionAttributes() {
        return ZkUtils.getCurrentSession().getAttributes();
    }
    
    /**
     * 获得会话超时事件，单位秒.
     * 
     * @return int
     */
    public static int getSessionMaxInactiveInterval() {
        return ZkUtils.getCurrentSession().getMaxInactiveInterval();
    }
    
    /**
     * 指定失效事件，单位秒，负值表示永不过期.
     * 
     * @param interval int
     */
    public static void setSessionMaxInactiveInterval(final int interval) {
        ZkUtils.getCurrentSession().setMaxInactiveInterval(interval);
    }
    
    /**
     * 销毁当前session.
     */
    public static void invalidateSession() {
        ZkUtils.getCurrentSession().invalidate();
    }
    
    /**
     * 设置页面作用域属性.
     * 
     * @param name
     *            属性名
     * @param value
     *            属性值
     */
    public static void setPageAttribute(final String name, final Object value) {
        ZkUtils.getCurrentPage().setAttribute(name, value);
    }
    
    /**
     * 设置page或父作用域属性.
     * 
     * @param name
     * @param value
     * @param recurse
     *            是否检索父作用域，如果为true， 并且当前作用域没声明这个属性，那么将搜索父作用域 ,并替换
     */
    public static void setPageAttribute(final String name, final Object value, final boolean recurse) {
        ZkUtils.getCurrentPage().setAttribute(name, value);
    }
    
    /**
     * 获得当前请求来自的页面.
     * 
     * @return String
     */
    public static String getRequestPagePath() {
        return ZkUtils.getCurrentPage().getRequestPath();
    }
    
    /**
     * 获得桌面作用域属性.
     * 
     * @param name String
     * @return Object
     */
    public static Object getDesktopAttribute(final String name) {
        return ZkUtils.getCurrentDesktop().getAttribute(name);
    }
    
    /**
     * 获得指定id的桌面.
     * <p>
     * 需要注意到是：例如在page1中包含iframe，iframe包含的页面为page2，那么zk将为page2新建一个桌面对象desktop2，
     * 因此page1与page2属于不同的桌面， 当你在page2的一个按钮或所属的其他组件触发的事件中
     * 使用该方法获得page1的子页面的时候，当前动作请求所属桌面为desktop2,而不是page1所属的desktop1，
     * 因此你无法从desktop2中查找属于desktop1的页面
     * 
     * @param pageId
     *            页面的id
     * @return 页面对戏那个
     * @see Desktop#getPage(String)
     */
    public static Page getPage(final String pageId) {
        return ZkUtils.getCurrentDesktop().getPage(pageId);
    }
    
    /**
     * forward.
     * 
     * @param page String
     * @throws IOException Exception
     */
    public static void forward(final String page) throws IOException {
        if (page == null) {
            Executions.forward(page);
        } else {
            final String npageUrl = ((page + "").indexOf("?") > -1) ? page
                + ZkUiUtil.getTimeSpan("&")
                : page + ZkUiUtil.getTimeSpan("?");
            Executions.forward(npageUrl);
        }
    }
    
    /**
     * sendRedirect.
     * 
     * @param page String
     */
    public static void sendRedirect(final String page) {
        if (page == null) {
            Executions.sendRedirect(page);
        } else {
            final String npageUrl = ((page + "").indexOf("?") > -1) ? page
                + ZkUiUtil.getTimeSpan("&")
                : page + ZkUiUtil.getTimeSpan("?");
            Executions.sendRedirect(npageUrl);
        }
    }
    
    /**
     *向当前execution提交一个事件.
     * <p>
     * 将事件提交到事件队列末尾，然后立即返回。 队列中排在前面的事件处理完毕后执行该动作提交的事件。
     * 
     * @param event Event
     */
    public static void postEvent(final Event event) {
        Events.postEvent(event);
    }
    
    /**
     *向当前execution提交一个事件，可以设置事件的优先级.
     * <p>
     * 将事件提交到事件队列末尾，然后立即返回。 队列中排在前面的事件处理完毕后执行该动作提交的事件。
     * 
     * @param priority int
     * @param event Event
     */
    public static void postEvent(final int priority, final Event event) {
        Events.postEvent(priority, event);
        
    }
    
    /**
     *向目标组件发送指定名称的事件.
     * <p>
     * 将事件提交到事件队列末尾，然后立即返回。 队列中排在前面的事件处理完毕后执行该动作提交的事件。
     */
    public static void postEvent(final String name, final Component target, final Object data) {
        Events.postEvent(name, target, data);
    }
    
    /**
     * 向当前execution发送一个事件.
     * <p>
     * 事件处理线程和调用该方法的线程为同一线程，即二者为相同线程，所以必须等待事件处理完毕，该方法才会返回。
     * <p>
     * 如果目标事件的的处理器，是一个长操作，那么当前线程将长事件阻塞，而在客户端表现为：左上角一直出现"正在处理，请稍候..."等字样的提示，
     * 所以在使用前注意
     * 
     * @param event
     */
    public static void sendEvent(final Event event) {
        Events.sendEvent(event);
    }
    
    /**
     * 向指定组件发送事件.
     * <p>
     * 事件处理线程和调用该方法的线程为同一线程，即二者为相同线程，所以必须等待事件处理完毕，该方法才会返回
     * <p>
     * 如果目标事件的的处理器，是一个长操作，那么当前线程将长事件阻塞，而在客户端表现为：左上角一直出现"正在处理，请稍候..."等字样的提示，
     * 所以在使用前注意
     * 
     * @param comp
     *            目标组件
     * @param event
     */
    public static void sendEvent(final Component comp, final Event event) {
        Events.sendEvent(comp, event);
    }
    
    /**
     * getRealOrigin.
     * 
     * @param event Event
     * @return Event
     */
    public static Event getRealOrigin(final ForwardEvent event) {
        return Events.getRealOrigin(event);
    }
    
    /**
     * <p>
     * 给指定的组件添加controller对象中定义的onXxx事件处理器，该controller是一个
     * 包含onXxx方法的POJO对象，该工具方法将onXxx方法注册给指定组件，因此你不用通过{@link EventListener}
     * 一个一个的向组件注册了.
     * </p>
     * <p>
     * 所有在controller对象中以"on"开头的公共方法被作为事件处理器，并且相关事件同时也被监听，例如，
     * 如果controller对象有一个名字为onOk的方法，那么 onOk事件将被监听，然后当接收到onOk事件的时候， onOk方法被调用
     * 
     * @param comp
     *            the component to be registered the events
     * @param controller
     *            a POJO file with onXxx methods(event handlers)
     * @since 3.0.6
     * @see GenericEventListener
     */
    public static void addEventListeners(final Component comp, final Object controller) {
        Events.addEventListeners(comp, controller);
    }
    
    /**
     * 检测名称时候一个合法的zk事件名.
     * 
     * @param name
     * @return boolean
     */
    public static boolean isValidEventName(final String name) {
        return Events.isValid(name);
    }
    
    /**
     * 判断一个指定事件的组件是否有事件处理器或监听器.
     * 
     * @param comp
     * @param evtnm
     * @param asap
     *            是否仅检测非延迟事件监听器，例如实现org.zkoss.zk.ui.event.Deferrable或
     *            org.zkoss.zk.ui.event.Deferrable.isDeferrable 返回 false的监听器
     * @return boolean
     */
    public static boolean isListened(final Component comp, final String evtnm, final boolean asap) {
        return Events.isListened(comp, evtnm, asap);
    }
    
    /**
     * 从uri指定的文件创建组件.
     * 
     * @param uri
     * @param parent
     *            创建的组件所属的父组件F
     * @param args
     *            创建组件传递的参数
     * @return 创建的组件，该组件对象为uri页面的第一个组件（zk节点除外）
     */
    public static Component createComponents(final String uri, final Component parent,
        final Map args) {
        return Executions.createComponents(uri, parent, args);
    }
    
    /**
     * 从zul格式字符串创建组件.
     * 
     * @param content
     *            zul格式内容的字符串
     * @param parent
     *            父组件，如果为null,那么组件所属的页面为当前页面，当前页面由execution上下文决定。另外新的组件将作为当前页面的根组件
     * @param args
     *            一个map类型的参数， 传递的参数可以使用Executions.getArgs()获得
     * @return 根据content创建的组件第一个组件
     */
    public static Component createComponentsDirectly(final String content, final Component parent,
        final Map args) {
        return Executions.createComponentsDirectly(content, "zul", parent, args);
    }
    
    /**
     * 重绘组件.
     * <p>
     * 仅允许在<b>请求处理阶段</b>和<b>事件处理阶段</b>调用， 不允许在<b>呈现阶段</b>调用
     * 
     * @param comp
     */
    public static void redraw(final Component comp) {
        comp.invalidate();
    }
    
    /**
     * 重绘页面.
     * <p>
     * 仅允许在<b>请求处理阶段</b>和<b>事件处理阶段</b>调用， 不允许在<b>呈现阶段</b>调用
     * 
     * @param page
     */
    public static void redrawPage(final Page page) {
        page.invalidate();
    }
    
    /**
     * 弹出消息提示框.
     * 
     * @param message
     *            提示消息
     * @param title
     *            提示框标题
     */
    public static void showInformation(final String message, final String title) {
        Messagebox.show(message, title, Messagebox.OK, Messagebox.INFORMATION);
    }
    
    /**
     * 弹出警告提示框.
     * 
     * @param message
     *            提示消息
     * @param title
     *            提示框标题
     */
    public static void showExclamation(final String message, final String title) {
        Messagebox.show(message, title, Messagebox.OK, Messagebox.EXCLAMATION);
    }
    
    /**
     * 弹出消息提示框.
     * 
     * @param message
     *            提示消息
     * @param title
     *            提示框标题
     */
    public static void showError(final String message, final String title) {
        Messagebox.show(message, title, Messagebox.OK, Messagebox.ERROR);
    }
    
    /**
     * 询问提示框.
     * <p>
     * 如果禁用事件处理线程，该方法会立即返回，返回值永远为true。 如果作为if判断语句的条件，
     * 那么else部分永远不会执行，启用和开启事件处理请查看zk.xml配置: <br />
     * &lt;system-config&gt;<br />
     * &lt;disable-event-thread&gt;false&lt;/disable-event-thread&gt;<br />
     * &lt;/system-config&gt;
     * 
     * @param message
     *            提示消息 提示框标题
     * @return 禁用事件处理线程该方法永远返回true，启用事件处理相称时，如果用户点击ok按钮，返回true,反之false
     */
    public static boolean showQuestion(final String message, final String title) {
        return Messagebox.OK == Messagebox.show(message, title, Messagebox.OK
		    | Messagebox.CANCEL, Messagebox.QUESTION);
    }
    
    /**
     * 询问提示框.
     * <p>
     * 该方法是一个类似 {@link #showQuestion(String, String)}
     * 的方法，但与其不同的是，当禁用事件处理线程时，该方法非常有用。
     * <p>
     * <p>
     * 示例:<br />
     * <hr>
     * 
     * <pre>
     * ZkUtils.showQuestion(&quot;您确定删除该记录吗？&quot;, &quot;询问&quot;, new EventListener() {
     *     &#064;Override
     *     public void onEvent(Event event) throws Exception {
     *         int clickedButton = (Integer) event.getData();
     *         if (clickedButton == Messagebox.OK) {
     *             // 用户点击的是确定按钮
     *         } else {
     *             // 用户点击的是取消按钮
     *         }
     *     }
     *     
     * });
     * </pre>
     * <hr>
     * <p>
     * <table border="1">
     *<tr>
     * <td>按钮名称</td>
     * <td>事件名称</td>
     * </tr>
     *<tr>
     * <td>确定</td>
     * <td>onOK</td>
     * </tr>
     *<tr>
     * <td>取消</td>
     * <td>onCancel</td>
     * </tr>
     *</table>
     * 
     * @param message 消息
     * @param title 标题
     * @param eventListener 事件监听
     */
    public static void showQuestion(final String message, final String title,
        final EventListener eventListener) throws InterruptedException {
        Messagebox.show(message, title, Messagebox.OK | Messagebox.CANCEL, Messagebox.QUESTION,
		    eventListener);
        
    }
    
    /**
     * 验证表单.
     * <p>
     * 需要input元素的constraint属性的支持
     * <p>
     * 例如 年龄&lt;textbox constraint=&quot;/^[0-9]*$/:仅允许输入数字&quot;/&gt;
     * 
     * @param formContainer
     *            Input元素公共
     * @return 如果验证成功返回true,否则返回false
     */
    public static boolean validateForm(final Component formContainer) {
        return ZkUtils.validateForm(formContainer, true);
    }
    
    /**
     * 验证表单.
     * <p>
     * 需要input元素的constraint属性的支持 例如 年龄 &lt;textbox
     * constraint=&quot;/^[0-9]*$/:仅允许输入数字&quot;/&gt;
     * 
     * @param formContainer
     *            Input元素公共组件，即需要验证的输入元素所在的公共容器组件
     * @param showError
     *            是否显示错误提示
     * @return 如果验证成功返回true,否则返回false
     */
    public static boolean validateForm(final Component formContainer, final boolean showError) {
        try {
            ZkUtils.validateForm0(formContainer, showError);
            return true;
        } catch (final Exception e) {
            return false;
        }
    }
    
    /**
     * validateForm0.
     * 
     * @param formContainer Component
     * @param showError boolean
     */
    private static void validateForm0(final Component formContainer, final boolean showError) {
        final List<Component> cList = formContainer.getChildren();
        if (cList == null || cList.size() < 1) {
            return;
        } else {
            for (final Component c : cList) {
                if (c instanceof InputElement && !((InputElement) c).isValid()) {
                    if (showError) {
                        ((InputElement) c).getText();
                    }
                    throw new RuntimeException("表单输入不正确！");
                } else {
                    ZkUtils.validateForm0(c, showError);
                }
            }
        }
    }
    
    /**
     * 结束常操作处理.
     * <p>
     * 一个业务操作可能要一段时间可以处理完成，在处理期间，又不想让用户操作界面，影响业务处理等，
     * 那么可以在前台事件中调用zk.startProcessing(),此时左上角出现提示框，"正在处理,请稍候..."
     */
    public static void endProcessing() {
        Clients.evalJavaScript("zk.endProcessing();");
    }
    
    /**
    * 根据key，返回execution作用域内参数.
    * 
    * @param key
    * @return Object
    * @see Execution#getArg()
    */
    public static Object getExecutionArg(final String key) {
        final Map args = ZkUtils.getCurrentExecution().getArg();
        return args == null ? null
            : args.get(key);
    }
    
    /**
     * 判断控件是否有效.
     * 
     * @param comp Component
     * @return boolean
     * @author wuyx
     * 2011-6-28 wuyx 
     */
    public static boolean isOnline(Component comp) {
        if (comp == null || comp.getPage() == null || comp.getPage().getDesktop() == null
            || comp.getPage().getDesktop().getWebApp() == null) {
            return false;
        }
        if (ZkUtils.getCurrentExecution() == null
            || ZkUtils.getCurrentExecution().getDesktop() == null
            || ZkUtils.getCurrentExecution().getDesktop() != comp.getPage().getDesktop()) {
            return false;
        }
        return true;
    }
    
    /**
     * 判断是否是界面.
     * 
     * @return boolean
     */
    public static boolean isOnUi() {
        return ZkUtils.getCurrentExecution() == null ? false
            : true;
    }
    
    /**
     * 判断是否是Ipad界面.
     * 
     * @return boolean
     */
    public static boolean isIpad(Component comp) {
        if(comp == null || comp.getDesktop() == null){
            return false;
        }
        String st = comp.getDesktop().getAttribute(PageConstants.SALE_TYPE) + "";
        return PageConstants.SALE_TYPE_IPAD.equals(st); // ipad
    }
    
    /**
     * 判断是否是Ipad界面.
     * 
     * @return boolean
     */
    public static void setIsIpad(Component comp) {
        comp.getDesktop().setAttribute(PageConstants.SALE_TYPE, PageConstants.SALE_TYPE_IPAD);
    }
    
    public static boolean isBrowser(String str) {
        return ZkUtils.getCurrentExecution().isBrowser(str);
    }
    
    public static boolean isIpadBrowser() {
        String userAgent = ZkUtils.getCurrentExecution().getUserAgent();
        return ("" + userAgent).toLowerCase().indexOf("ipad") > -1;
    }
    
}
